package com.investmenttechnologyservices.services;

import static com.google.common.base.Preconditions.checkState;

import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Currency;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletException;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.PreparedQuery;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Transaction;
import com.google.inject.Inject;
import com.investmenttechnologyservices.model.CashInstrument;
import com.investmenttechnologyservices.model.InstrumentType;

public class InstrumentServiceImpl implements InstrumentService {

	private final DatastoreService ds;
	private final SymbolService symbolService;
	
	@Inject
	InstrumentServiceImpl(DatastoreService ds, SymbolService symbolService) {
		this.ds = ds;
		this.symbolService = symbolService;
	}

	/**
	 * returns a list of Maps which represent instruments
	 * each map contains the following properties: 
	 * id - the string id of the instrument
	 * name - the instrument name 
	 * 
	 * @return list of instruments
	 * 
	 * @throws ServletException
	 */
	public List<Map<String,Object>> findAll() throws ServletException {
		try {
			Query q = new Query("instrument");
			PreparedQuery pq = ds.prepare(q);
			Iterable<Entity> iter = pq.asIterable();
			List<Map<String,Object>> ret = new ArrayList<Map<String,Object>>();
			for (Entity entity : iter) {
				Map<String,Object> element = new HashMap<String, Object>();
				Key instrumentKey = entity.getKey();
				element.put("id", instrumentKey.getName());
				element.put("name", entity.getProperty("name"));
				ret.add(element);
			}
			return ret;
		} catch (Exception ex) {
			throw new ServletException(ex);
		}
	}

	@Override
	public Object createClassFromEntity(Entity entity) {
		Class<?> newClass = null;
		Object newObject = null;
		Constructor<?> cons = null;

		Class<?>[] parameter = new Class<?>[]{Entity.class};
		Object[] values = new Entity[]{entity};
		
		String instrumentTypeString = (String) entity.getProperty("instrumentType");
		if (instrumentTypeString == null)
			instrumentTypeString = "EQUITY";
		
		InstrumentType instrumentType = InstrumentType.valueOf(instrumentTypeString);
		
		try {
			 newClass = Class.forName("com.investmenttechnologyservices.model."+instrumentType.getClassName());
			 cons = newClass.getConstructor(parameter);
			 newObject = cons.newInstance(values);
			 
			 System.out.println("The object' hashcode is : " + newObject.hashCode());
		 } catch (ClassNotFoundException e) {
			 e.printStackTrace();
		 } catch (InstantiationException e) {
			 e.printStackTrace();
		 } catch (IllegalAccessException e) {
			 e.printStackTrace();
		 } catch (Exception e) {
			 e.printStackTrace();
		 }
		return newObject;
	}
	
	@Override
	public Key getCashInstrument(Currency currency, InstrumentType instrumentType) {
		Key key = KeyFactory.createKey("instrument", currency.getCurrencyCode()+"_"+
				instrumentType.name());
		try {
			Entity entity = ds.get(key);
		} catch (EntityNotFoundException ex) {
			try {
				key = createCashInstrument(currency, instrumentType);
			} catch (IllegalStateException e) {
				e.printStackTrace();
			}
		}
		return key;
	}

	private Key createCashInstrument(Currency currency, InstrumentType instrumentType) {
		checkState(instrumentType.getIsCash(),"Only cash instrument types!");
		CashInstrument cashInstrument = new CashInstrument(
				currency.getCurrencyCode()+"_"+instrumentType.name(), 
				null, 
				currency.getCurrencyCode()+"("+instrumentType.getClassName()+")", 
				null, 
				currency,
				instrumentType);
		Transaction tx = ds.beginTransaction();
		ds.put(cashInstrument.asEntity());
		tx.commit();
		
		return cashInstrument.getKey();
	}
}
